package org.dreamwork.tools.code.analyzer.core.service.impl;

import org.dreamwork.db.ITransaction;
import org.dreamwork.db.SQLite;
import org.dreamwork.tools.code.analyzer.core.FileStatistician;
import org.dreamwork.tools.code.analyzer.core.ModuleStatisticInfo;
import org.dreamwork.tools.code.analyzer.core.ModuleWalker;
import org.dreamwork.tools.code.analyzer.core.data.Entity;
import org.dreamwork.tools.code.analyzer.core.service.IEntityService;
import org.dreamwork.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import java.io.IOException;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import static org.dreamwork.tools.code.analyzer.core.model.EntityType.project;

@Resource
public class EntityServiceImpl extends AbstractBaseServiceImpl implements IEntityService {
    private final Logger logger = LoggerFactory.getLogger (EntityServiceImpl.class);

    private static final String SQL_FIND_MODULE_BY_PROJECT = "SELECT _id FROM _entity WHERE _belongs_to = ?";

    @Resource (name = "sqlite/analyzer")
    private SQLite sqlite;

    @Override
    public List<Entity> loadAllProjects () {
        return sqlite.get (Entity.class, "_type = ?", "_name ASC", "project");
    }

    @Override
    public List<Entity> loadAllModules (String projectId) {
        return sqlite.get (Entity.class, "_belongs_to = ?", "_name ASC", projectId);
    }

    @Override
    public void saveOrUpdate (Entity entity) {
        boolean create = false;
        if (StringUtil.isEmpty (entity.getId ())) {
            create = true;
            entity.setId (StringUtil.uuid ());
        }

        if (create) {
            sqlite.save (entity);
        } else {
            sqlite.update (entity);
        }
    }

    @Override
    public void save (Entity project, List<Entity> modules) {
        ITransaction tx = null;
        try {
            tx = sqlite.beginTransaction ();
            tx.save (project, false);
            if (modules != null)
                tx.save (modules, false);
            tx.commit ();
        } catch (Exception ex) {
            if (tx != null) try {
                tx.rollback ();
            } catch (SQLException ignore) {}
        } finally {
            if (tx != null) try {
                tx.close ();
            } catch (IOException ignore) {}
        }
    }

    @Override
    public void statistic (Entity entity) throws SQLException {
        ITransaction tx = null;
        try {
            tx = sqlite.beginTransaction ();
            _clear (tx, entity);

            tx.commit ();
        } catch (RuntimeException ex) {
            if (tx != null) try {
                tx.rollback ();
            } catch (SQLException ignore) {}
            throw ex;
        } finally {
            if (tx != null) try {
                tx.close ();
            } catch (IOException ignore) {}
        }
    }

    @Override
    public void clear (Entity entity) throws SQLException {
        ITransaction tx = null;
        try {
            tx = sqlite.beginTransaction ();
            _clear (tx, entity);
            tx.commit ();
        } catch (RuntimeException ex) {
            logger.warn (ex.getMessage (), ex);
            if (tx != null) {
                try {
                    tx.rollback ();
                } catch (SQLException ignore) {}
            }
            throw ex;
        } finally {
            if (tx != null) try {
                tx.close ();
            } catch (IOException ignore) {}
        }
    }

    private void _clear (ITransaction tx, Entity entity) throws SQLException {
        List<String> modules = null;
        if (project == entity.getType ()) {
            try (Connection conn = sqlite.connect ()) {
                PreparedStatement pstmt = conn.prepareStatement (SQL_FIND_MODULE_BY_PROJECT);
                pstmt.setString (1, entity.getId ());
                ResultSet rs = pstmt.executeQuery ();
                modules = new ArrayList<> ();
                while (rs.next ()) modules.add (rs.getString (1));
            }
        }
        if (entity.getType () == project && modules != null) {
            for (String module : modules) {
                _clear (tx, module);
            }
        } else {
            _clear (tx, entity.getId ());
        }
    }

    private void _clear (ITransaction tx, String moduleId) {
        String sql = "DELETE FROM _detail WHERE _belongs_to = ?";
        tx.executeUpdate (sql, moduleId);
        sql = "DELETE FROM _counted WHERE _entity_id = ?";
        tx.executeUpdate (sql, moduleId);
    }

    private void _statisticModule (ITransaction tx, Entity module, ModuleStatisticInfo info) {

    }
}